#include "stdafx.h"

#define CAPAPI __stdcall
typedef void*				CodeAddress;

extern "C" 
{
void CAPAPI _CAP_Start_Profiling(CodeAddress callerAddress, CodeAddress targetAddress)
{
	int a = (int)callerAddress;
	int b = (int)targetAddress;
}

void CAPAPI _CAP_End_Profiling(CodeAddress callerAddress)
{
	int a = (int)callerAddress;
}
}







//#ifdef _CALL_CAP
#if 0

typedef void*				CodeAddress;

extern int gCallCapEnabled = 0;


using namespace OS;


#ifdef PLATFORM_WIN32_PC
#	define CAPAPI __stdcall
#else
#	define CAPAPI
#endif

extern "C" 
{
	extern void CAPAPI _CAP_Start_Profiling_Ignored(CodeAddress callerAddress, CodeAddress targetAddress);
	extern void CAPAPI _CAP_Start_Profiling(CodeAddress callerAddress, CodeAddress targetAddress);
	extern void CAPAPI _CAP_End_Profiling(CodeAddress callerAddress);
}


#ifdef __cplusplus
extern "C"
#endif
void* _ReturnAddress(void);
#pragma intrinsic(_ReturnAddress)


#ifdef PLATFORM_WIN32_PC

	#define SaveStack()	__asm { pushad };
	#define RestoreStack()	__asm { popad };
	#define CAPAPI __stdcall
#else
	#define SaveStack()
	#define RestoreStack()
	#define CAPAPI

#endif



class PROFILING_DISABLER
{
/*
	// to init ThreadDataIndex_
	static PROFILING_DISABLER			Instance_;
	// store count of levels ignored for every thread
	static DWORD						ThreadDataIndex_;


public:
	PROFILING_DISABLER()
	{
		ThreadDataIndex_= ::TlsAlloc();
	}

	~PROFILING_DISABLER()
	{
		::TlsFree(ThreadDataIndex_);
		ThreadDataIndex_= TLS_OUT_OF_INDEXES;
	}
*/

public:
	static BOOL DisableProfiling(CodeAddress startReturnAddress/*, CodeAddress endReturnAddress*/);

/*
	FORCEINLINE static void StartIgnored()
	{
		DWORD ignoredCount= (DWORD)::TlsGetValue(ThreadDataIndex_);
		++ignoredCount;
		::TlsSetValue(ThreadDataIndex_, (void*)ignoredCount);
	}

	FORCEINLINE static BOOL IsIgnoredStart()
	{
		DWORD ignoredCount= (DWORD)::TlsGetValue(ThreadDataIndex_);
		if (ignoredCount == 0)
			return FALSE;

		++ignoredCount;
		::TlsSetValue(ThreadDataIndex_, (void*)ignoredCount);
		return TRUE;
	}

	FORCEINLINE static BOOL IsIgnoredEnd()
	{
		DWORD ignoredCount= (DWORD)::TlsGetValue(ThreadDataIndex_);
		if (ignoredCount == 0)
			return FALSE;

		--ignoredCount;
		::TlsSetValue(ThreadDataIndex_, (void*)ignoredCount);
		return TRUE;
	}
*/
};

/*
PROFILING_DISABLER			PROFILING_DISABLER::Instance_;
DWORD						PROFILING_DISABLER::ThreadDataIndex_= TLS_OUT_OF_INDEXES;
*/


BOOL PROFILING_DISABLER::DisableProfiling(CodeAddress startReturnAddress/*, CodeAddress endReturnAddress*/)
{
#if (defined PLATFORM_WIN32_PC)

	/*
	// B plan: replace call to _CAP_Start_Profiling with call to _CAP_Start_Profiling_Ignored

	BYTE* startTo= static_cast<BYTE*>( startReturnAddress );
	BYTE* endTo= static_cast<BYTE*>( endReturnAddress );

	// strictly 32-bit compatible
	// call func == 0xe8, relative address on 4 bytes
	// startTo points to the next instruction
	BYTE* startFuncRelAddress= startTo - 4;

	// check call opcode for safety
	if (startFuncRelAddress[-1] == 0xe8)
	{
		DWORD& relAddress= *reinterpret_cast<DWORD*>( startFuncRelAddress );

		// make code segment writeable
		DWORD oldProtect;
		BOOL success= VirtualProtect(&relAddress, sizeof(relAddress), PAGE_EXECUTE_READWRITE, &oldProtect);
		THMON_ASSERT(success, "DisableProfiling(): Failed to make code segment writeable");

		if (success)
		{
			// call _CAP_Start_Profiling_Ignored instead of _CAP_Start_Profiling
			static const BYTE* origFunc= (BYTE*)&_CAP_Start_Profiling;
			static const BYTE* ignoredFunc= (BYTE*)&_CAP_Start_Profiling_Ignored;
			static const ptrdiff_t diff= ignoredFunc - origFunc;

			relAddress+= diff;

			return TRUE;
		}
	}
	else
	{
		THMON_ASSERT(false, "DisableProfiling(): call instruction opcode not found at the expected position");
	}

	return FALSE;
	*/

	/*
	// original concept:
	// call _CAP_Start_Profiling always has a unique call _CAP_End_Profiling pair
	// replace both with stack adjustment (remove parameters) and NOP
	// it turned out that the compiler may merge calls to _CAP_End_Profiling before returns from a function
	// so the concept failed
	THMON_ASSERT(endTo - startTo < 0x20, "DisableProfiling(): Sanity check for start - end call locality failed");

	enum { CALL_INSTR_SIZE= 5 };
	static const BYTE		startCode[CALL_INSTR_SIZE]=	{ 0x83, 0xc4, 0x08, 0x90, 0x90 };	// add esp,8; nop; nop
	static const BYTE		endCode[CALL_INSTR_SIZE]=	{ 0x83, 0xc4, 0x04, 0x90, 0x90 };	// add esp,4; nop; nop

	// call 5 bytes before returnAddress
	BYTE* startFrom= startTo - CALL_INSTR_SIZE;
	BYTE* endFrom= endTo - CALL_INSTR_SIZE;

	// check call opcode for safety
	if (startFrom[0] == 0xe8 && endFrom[0] == 0xe8)
	{
		ptrdiff_t len= endTo - startFrom;
		DWORD oldProtect;
		BOOL success= VirtualProtect(startFrom, len, PAGE_EXECUTE_READWRITE, &oldProtect);
		THMON_ASSERT(success, "DisableProfiling(): Failed to make code segment writeable");

		if (success)
		{
			const BYTE* code= startCode;
			for (; startFrom != startTo; ++startFrom, ++code)
				*startFrom= *code;
			code= endCode;
			for (; endFrom != endTo; ++endFrom, ++code)
				*endFrom= *code;
		}
	}
	else
	{
		THMON_ASSERT(false, "DisableProfiling(): call instruction opcode not found at the expected position");
	}
	*/

	enum { CALL_INSTR_SIZE= 5 };
	static const BYTE		startCode[CALL_INSTR_SIZE]=	{ 0x83, 0xc4, 0x08, 0x90, 0x90 };	// add esp,8; nop; nop

	// call 5 bytes before returnAddress
	BYTE* startTo= static_cast<BYTE*>( startReturnAddress );
	BYTE* startFrom= startTo - CALL_INSTR_SIZE;

	// check call opcode for safety
	if (startFrom[0] == 0xe8)
	{
		DWORD oldProtect;
		BOOL success= VirtualProtect(startFrom, CALL_INSTR_SIZE, PAGE_EXECUTE_READWRITE, &oldProtect);
		THMON_ASSERT(success, "DisableProfiling(): Failed to make code segment writeable");

		if (success)
		{
			const BYTE* code= startCode;
			for (; startFrom != startTo; ++startFrom, ++code)
				*startFrom= *code;
		}
	}
	else
	{
		THMON_ASSERT(false, "DisableProfiling(): call instruction opcode not found at the expected position");
	}

	return FALSE;

#elif (defined PLATFORM_WIN32_WCE)
	
	DWORD* startTo= static_cast<DWORD*>( startReturnAddress );
	DWORD* startFrom= startTo - 1;

	struct BranchInstruction
	{
		signed		relAddressDiv4_	:24;
		unsigned	opcode_			:8;
	};

	// startTo points to the next instruction
	BranchInstruction& instruction= *reinterpret_cast<BranchInstruction*>( startFrom );

	// check call opcode for safety: unconditional BL == 0xeb
	if (instruction.opcode_ == 0xeb)
	{
		// make code segment writeable
		DWORD oldProtect;
		BOOL success= VirtualProtect(startFrom, sizeof(*startFrom), PAGE_EXECUTE_READWRITE, &oldProtect);
		THMON_ASSERT(success, "DisableProfiling(): Failed to make code segment writeable");

		if (success)
		{
			// do NOP instead of call; no stack adjustment needed, parameters are in r0, r1, no return value expected in r0
			// NOP: MOV r0, r0
			*startFrom= 0xe1a00000;

			return TRUE;
		}
	}
	else
	{
		THMON_ASSERT(false, "DisableProfiling(): call instruction opcode not found at the expected position");
	}

	return FALSE;


#endif
}



extern "C" 
{
	// callcap
	// replaced with fastcap:
	// callcap fucked up parameter passing in 'vector constructor iterator'
	// parameters were loaded from real stack position -4 byte
	// causing a CALL to wrong function address (parameter 4 is the address of the scalar constructor)



	/*
	void CAPAPI _CAP_Start_Profiling_Ignored(CodeAddress callerAddress, CodeAddress targetAddress)
	{
		SaveStack();

		// function signature must be the same to properly adjust stack pointer on return
		MAKE_REFERENCED(callerAddress);
		MAKE_REFERENCED(targetAddress);

		PROFILING_DISABLER::StartIgnored();

		RestoreStack();
	}
	*/

	// fastcap
	// used also on Win32 (PLATFORM_WIN32_PC)
	// contrary to MSDN documentation fastcap works also when compiling for x86
	// in VS8 SP1 -- Visual Studio 2005 Version 8.0.50727.762 (SP.050727-7600)

	void CAPAPI _CAP_Start_Profiling(CodeAddress callerAddress, CodeAddress targetAddress)
	{
		/**
		* callerAddress - the function initiating the call
		* targetAddress - the function receiving the call
		*/
		SaveStack();

		if (/*!PROFILING_DISABLER::IsIgnoredStart() && */gCallCapEnabled == 0 && THREAD_MONITOR_SINGLETON::IsCreated())
		{
			CALL_STACK_ENTRY call(callerAddress, targetAddress);
#ifdef FFCONFIG_CALL_CAP_DISABLER
			CodeAddress returnAddress= _ReturnAddress();
			call.SetReturnAddress(returnAddress);
#endif
			THREAD_MONITOR_SINGLETON::GetInstance().EnterFunction(call);
		}

		RestoreStack();
	}


	static CALL_STACK_ENTRY		disabled[1 << 16];
	static DWORD				disabledCount= 0;

	void CAPAPI _CAP_End_Profiling(CodeAddress callerAddress)
	{
		SaveStack();

		if (/*!PROFILING_DISABLER::IsIgnoredEnd() && */gCallCapEnabled == 0 && THREAD_MONITOR_SINGLETON::IsCreated())
		{
			DisableEntryType disableEntry= THREAD_MONITOR_SINGLETON::GetInstance().LeaveFunction(callerAddress);
#ifdef FFCONFIG_CALL_CAP_DISABLER
			if (disableEntry)
			{
				BOOL success= PROFILING_DISABLER::DisableProfiling(disableEntry->GetReturnAddress());
				if (disabledCount < ARRAY_SIZE(disabled))
				{
					disabled[disabledCount].CopyAddresses(*disableEntry);
					++disabledCount;
				}
			}
#endif
		}

		RestoreStack();
	}

}



#endif // #ifdef _CALL_CAP


